use algtype::Generic;
use count_enum::{iter_each, Enum, GenericEnum};
use power_map::{GenericMapKey, MapKey};
use serde_json::{json, Value};
use std::fmt::Debug;

pub trait Skill: Sized + MapKey + Enum + Copy + PartialEq + Debug {
    /// 花费，可以为负（说明能赚）
    fn cost(self) -> i8;
    /// 能赢啥
    fn beats(&self) -> &[Self];

    /// 返回自己是否胜利或平局
    fn versus(self, other: Self) -> Option<bool> {
        match (self.beats().contains(&other), other.beats().contains(&self)) {
            (false, false) => None,
            (false, true) => Some(false),
            (true, false) => Some(true),
            (true, true) => unreachable!(),
        }
    }

    fn all_enough(ji: usize) -> impl Iterator<Item = Self> + Clone {
        iter_each::<Self>().filter(move |s| s.enough(ji))
    }

    fn enough(self, ji: usize) -> bool {
        ji >= self.cost().try_into().unwrap_or(0)
    }

    fn list() {
        iter_each().for_each(|skill: Self| {
            println!("技能：{skill:?}");

            let cost = skill.cost();
            if cost < 0 {
                println!("  可以获得 {} 个集", -cost);
            } else {
                println!("  需要消耗 {cost} 个集");
            }

            let lose: Vec<_> = iter_each()
                .filter(|&s| skill.versus(s) == Some(false))
                .collect();
            println!("  能够战胜这些：{:?}", skill.beats());
            println!("  会被这些打败：{lose:?}");
        });
    }

    fn encode() -> Value {
        let arr = iter_each::<Self>().map(|s| {
            let versus = iter_each().map(|s2| {
                json!(match s.versus(s2) {
                    Some(true) => 1,
                    Some(false) => -1,
                    None => 0,
                })
            });
            json!({
                "name": format!("{s:?}"),
                "cost": s.cost(),
                "versus": Value::Array(versus.collect())
            })
        });
        Value::Array(arr.collect())
    }
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Generic)]
pub enum Normal {
    集,
    小防,
    中防,
    大防,
    小波,
    中波,
    大波,
    黑洞,
}

impl Skill for Normal {
    fn cost(self) -> i8 {
        use Normal::*;
        match self {
            集 => -1,
            小防 => 0,
            中防 => 0,
            大防 => 1,
            小波 => 1,
            中波 => 2,
            大波 => 3,
            黑洞 => 5,
        }
    }

    fn beats(&self) -> &'static [Self] {
        use Normal::*;
        match self {
            集 | 小防 | 大防 | 中防 => &[],
            小波 => &[集, 中防],
            中波 => &[集, 小波, 小防],
            大波 => &[集, 小波, 小防, 中波, 中防],
            黑洞 => &[集, 小波, 小防, 中波, 中防, 大波, 大防],
        }
    }
}

impl GenericEnum for Normal {}
impl GenericMapKey for Normal {}
